// 类
function class_eg01() {
  class Greeter {
    greeting: string;
    constructor(msg: string) {
      this.greeting = msg;
    }
    greet() {
      return 'Hello, ' + this.greeting;
    }
  }

  let greeter = new Greeter("world");

  let resMsg = greeter.greet()
  console.log(resMsg, greeter)
}

// 继承
function class_eg02() {
  class Animal { // 基类（超类）
    move(distanceInMeters: number = 0) {
      console.log(`Animal moved ${distanceInMeters}m.`);
    }
  }

  class Dog extends Animal { // 子类（派生类）
    bark() {
      console.log('Woof! Woof!');
    }
  }

  const dog = new Dog();
  dog.bark();
  dog.move(100);
}

/*
这个例子展示了最基本的继承：类从基类中继承了属性和方法。 这里， Dog是一个 派生类，它派生自 Animal 基类，通过 extends关键字。 派生类通常被称作 子类，基类通常被称作 超类。
*/

// 必须调用 super()，它会执行基类的构造函数。
function class_eg03() {
  class Animal {
    name: string;
    constructor(theName: string) { this.name = theName; }
    move(distanceInMeters: number = 0) {
      console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
  }

  class Snake extends Animal {
    constructor(name: string) {
      super(name)
    }
    move(distanceInMeters: number = 5) {
      console.log('Slithering...');
      super.move(distanceInMeters);
    }
  }

  class Horse extends Animal {
    constructor(name: string) {
      super(name)
    }
    move(distanceInMeters = 45) {
      console.log("Galloping...");
      super.move(distanceInMeters);
    }
  }

  let sam = new Snake("Sammy the Python");

  let tom: Animal = new Horse("Tommy the Palomino");

  sam.move();
  tom.move(56);
}

// 公共，私有与受保护的修饰符
/*
1、默认为 public
2、理解 private，当成员被标记成 private时，它就不能在声明它的类的外部访问。
3、理解 protected，protected修饰符与 private修饰符的行为很相似，但有一点不同， protected成员在派生类中仍然可以访问。
4、readonly修饰符，你可以使用 readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
*/

function class_eg04() {
  class Person {
    protected name: string;
    constructor(name: string) {
      this.name = name;
    }
  }

  class Employee extends Person {
    private department: string;
    constructor(name: string, department: string) {
      super(name);
      this.department = department;
    }

    public getElevatorPitch() {
      return `Hello, my name is ${this.name}, and I work in ${this.department}`;
    }
  }

  let howard = new Employee("Howard", "Sales");
  console.log(howard.getElevatorPitch());
  // console.log(howard.name)
}


// 抽象类：抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口，抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
// 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而，抽象方法必须包含 abstract关键字并且可以包含访问修饰符。
function class_eg05() {
  abstract class Department {
    constructor(public name: string) { }

    printName(): void {
      console.log(`DepartName name: ${this.name}`);
    }

    abstract printMeeting(): void;
    // abstract generateReports(): void;
  }

  class AccountingDepartment extends Department {
    constructor() {
      super('Accounting and Auditing');
    }

    printMeeting(): void {
      console.log('The Accounting Department meets each Monday at 10am.');
    }

    generateReports(): void {
      console.log('Generating accounting reports...');
    }
  }

  let department: Department; // 允许创建一个抽象类的引用
  // department = new Department(); // 错误：不能创建一个抽象类的实例
  department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
  department.printName();
  department.printMeeting();
  // department.generateReports(); // 方法在声明的抽象类中不存在
}

// 把类当做接口使用
function class_eg06() {
  class Point {
    x: number;
    y: number;
  }

  interface Point3d extends Point {
    z: number;
  }

  let point3d: Point3d = { x: 1, y: 2, z: 3 };
}

// class_eg01();
// class_eg02();
// class_eg03();
class_eg04();